﻿using System;
using System.IO;
using System.Collections.Generic;
using XAMSDK;
using XAMSDK.Types;
using System.Threading;

namespace TestDriver
{
    class Program
    {
        static XUID xuid = new XUID();
        static XSystem xsys;
        static XSet xset;

        static void ViewFields(XAMObject inXAMObject, string inPrefixFilter)
        {
            using (XIterator xit = inXAMObject.CreateXIterator(inPrefixFilter))
            {
                while (xit.MoveNext())
                {
                    Console.WriteLine(inXAMObject.GetFieldInfo((XString) xit.Current));
                }
            }
        }

        static void XSetOpenCB(XAsyncHandle h)
        {
            // Can get the associated XAsyncObject via an implicit cast or explicitly find is using XAsyncObject.Get(h)
            XAsyncObject ao = h;
            Console.WriteLine("CB Open");
        }

        static void XSetCommitCB(XAsyncHandle h)
        {
            XAsyncObject ao = XAsyncObject.Get(h);
            Console.WriteLine("CB Commit " + ao.XUID);
        }

        static void XStreamCloseCB(XAsyncHandle h)
        {
            XAsyncObject ao = XAsyncObject.Get(h);
            Console.WriteLine("CB StreamClose");
        }

        static void XStreamReadCB(XAsyncHandle h)
        {
            XAsyncObject ao = XAsyncObject.Get(h);
            Console.WriteLine("CB Bytes Read " + ao.BytesRead);
        }

        static void XStreamWriteCB(XAsyncHandle h)
        {
            XAsyncObject ao = XAsyncObject.Get(h);
            Console.WriteLine("CB Bytes Written " + ao.BytesWritten);
        }


        static XUID AsyncCommitTest()
        {
            long opID = 23456;
            Console.Write("Async XSet commit ...");
            XAsyncObject ao = xset.Commit(opID, new XAsyncCallback(XSetCommitCB));

            while (!ao.Completed) ;
            XUID xuid = ao.XUID;

            return xuid;
        }

        static void AsyncStreamCloseTest(XStream xs)
        {
            long opID = 34567;
            Console.Write("Async XStream close ...");

            XAsyncObject ao = xs.Close(opID, new XAsyncCallback(XStreamCloseCB));
            while (!ao.Completed) ;
        }

        static void AsyncStreamReadTest(XStream xs)
        {
            byte[] buffer = new byte[64];
            long opID = 45678;
            long size = 64;
            Console.Write("Async Read ...");

            XAsyncObject ao = xs.Read(buffer, size, opID, new XAsyncCallback(XStreamReadCB));
            while (!ao.Completed) ;
        }

        static void AsyncStreamWriteTest(XStream xs)
        {
            long opID = 56789;
            long size = 88;
            byte[] buffer = new byte[88];
            Console.Write("Async Write ...");

            XAsyncObject ao = xs.Write(buffer, size, opID, new XAsyncCallback(XStreamWriteCB));
            while (!ao.Completed) ;
        }

        static void XSetCreateTest()
        {
            xset = xsys.CreateXSet();
            // Don;t use a double with more than 6 digits of precision (after the decimal point)
            // as this causes Export / Import mismatch just now (fixed in 1.1).
            // XSet.CreateField("doubleValue", Math.E);
            xset.CreateField("doubleValue", (double)123456.789321);
            xset.CreateField("value2", "this is a test string");
            xset.CreateField("value3", 34);
            xset.CreateField("longValue", (long)2345678);
            xset.CreateField("dateValue", DateTime.Now);

            //xset.AutoDelete = false;

            byte[] vDataArray = new byte[200 * 1024];
            for (int i = 0; i < vDataArray.Length; i++)
            {
                vDataArray[i] = (byte)(1 + i);
            }

            XStream xstream = xset.CreateXStream("stream1", Constants.DEFAULT_MIME_TYPE);

            xstream.Write(vDataArray, 0, vDataArray.Length);
            xstream.Dispose();
            xuid = xset.Commit();
            xset.Dispose();
        }

        static void XSetOpenTest()
        {
            xset = xsys.OpenXSet(xuid, Constants.XSET_MODE_MODIFY);

            //bool fieldValue;
            //xset.GetField(".xset.deletion.autodelete", out fieldValue);
            //Console.WriteLine("Auto delete " + fieldValue); 
                
            if (xset.ContainsField("stream1"))
            {
                using (XStream xs = xset.OpenXStream("stream1", Constants.XSTREAM_MODE_READ_ONLY))
                {
                    byte[] buffer = new byte[100];
                    xs.Read(buffer, 0, 100);
                    xs.Close();
                }
            }

            ViewFields(xset, "");

            // There are several ways to get back Field values - this way is quite neat (even if it does need a cast)
            DateTime commitTime = (DateTime)xset.GetFieldInfo(Constants.XSET_TIME_COMMIT).Value;
            Console.WriteLine("Clip committed on " + commitTime);
            Console.WriteLine("XUID " + xset.Commit());
            xset.Dispose();
        }

        static XUID AsyncCreateTest()
        {
            Console.Write("Async XSet create ...");
            xset = xsys.CreateXSet();
            xset.CreateField("doubleValue", Math.E);
            xset.CreateField("value2", "this is a test string");
            xset.CreateField("value3", 34);
            xset.CreateField("dateValue", DateTime.Now);

            XStream xstream = xset.CreateXStream("stream1", Constants.DEFAULT_MIME_TYPE);
            AsyncStreamWriteTest(xstream);
            AsyncStreamCloseTest(xstream);

            XUID xuid = AsyncCommitTest();
            return xuid;
        }

        static XSet AsyncOpenTest()
        {
            long opID = 12345;
            Console.Write("Async XSet open ...");

            XAsyncObject ao = xsys.OpenXSet(xuid, Constants.XSET_MODE_READ_ONLY, opID, new XAsyncCallback(XSetOpenCB));
            while (!ao.Completed) ;

            xset = ao.XSet;
            XStream xs = xset.OpenXStream("stream1", Constants.XSTREAM_MODE_READ_ONLY);
            AsyncStreamReadTest(xs);
            AsyncStreamCloseTest(xs);
            return xset;
        }

        internal class MyQuery : XQuery
        {
            internal MyQuery(XSystem xsys, XString query): base(xsys, query) { }

            // An example of processing the results in a different way
            public override bool ProcessResults()
            {
                byte[] xuid = new byte[Constants.XUID_SZ];
                //int bytesRead = 0;
              
                if (!Healthy)
                {
                    return false;
                }

                if (ResultCount > 0)
                {
                    Console.Write("Processing Results: Count " + ResultCount);

                    if (xset.ContainsField(Constants.JOB_QUERY_RESULTS))
                    {
                        if (resultStream == null)
                            resultStream = xset.OpenXStream(Constants.JOB_QUERY_RESULTS, Constants.XSTREAM_MODE_READ_ONLY);

                        Console.WriteLine(" Length of stream " + resultStream.Length);

                        // Read the whole stream into a local memory stream for processing
                        // as multiple small reads from the XStream are not efficient
                        BinaryReader br = new BinaryReader(resultStream);
                        MemoryStream ms = new MemoryStream((int)resultStream.Length);
                        
                        int bytesToRead = (int) resultStream.Length;
                        int readAttempt = 0;

                        do
                        {
                            byte[] buffer = br.ReadBytes(bytesToRead);
                            ms.Write(buffer, 0, buffer.Length);

                            bytesToRead -= buffer.Length;

                            if (bytesToRead == resultStream.Length)
                            {
                                // We have not managed to properly read the results stream
                                // Possible timing issue? Re-open it for the next attempt
                                resultStream.Close();
                                resultStream = xset.OpenXStream(Constants.JOB_QUERY_RESULTS, Constants.XSTREAM_MODE_READ_ONLY);
                                br = new BinaryReader(resultStream);
                                ms = new MemoryStream((int)resultStream.Length);
                                bytesToRead = (int)resultStream.Length;

                                readAttempt++;
                                Console.WriteLine("Failed to read any data from results stream - reopening and trying again");
                            }
                        } while (bytesToRead > 0 && readAttempt < 10);

                        br = new BinaryReader(ms);
                        br.BaseStream.Seek(0, SeekOrigin.Begin);

                        do
                        {
                            xuid = br.ReadBytes(Constants.XUID_SZ);
                            // Add the XUID to the collection

                            if (xuid.Length == Constants.XUID_SZ)
                            {
                                XUID resultXUID = new XUID(xuid);
                                ResultList.Add(resultXUID);
                            }
                        } while (br.BaseStream.Position != br.BaseStream.Length);
                    }

                    Console.WriteLine();
                }

                return true;
            }
        }      
 
        static void QueryTest()
        {
            XString[] queries = { "", 
                "where \".xset.time.creation\" > date('2008-11-25T08:00:00.0')",
                "where exists(\"testAttr\")", 
                "where \"com.emc.centera.meta.custom.testAttr\" = 'graham'",
                "where \".vnd.com.emc.centera.meta.standard.binding.clip.name\" = 'graham'", 
                "where \".vnd.com.emc.centera.meta.standard.binding.name\" = 'graham' or \"xsetname\" = 'graham'",
                "where \".xset.xuid\" = xuid('" + xuid + "')" };

            Console.WriteLine("\nSelect the query to run\n");
            Console.WriteLine("1 " + queries[0]);
            Console.WriteLine("2 " + queries[1]);
            Console.WriteLine("3 " + queries[2]);
            Console.WriteLine("4 " + queries[3]);
            Console.WriteLine("5 " + queries[4]);
            Console.Write("\nEnter choice: ");

            String answer = Console.ReadLine();
            MyQuery query;
            short choice;

            Console.Write("\nSubmitting query: select \".xset.xuid\" ");

            if (Int16.TryParse(answer, out choice))
                answer = queries[choice - 1];

            Console.WriteLine(answer);
            query = new MyQuery(xsys, answer);
            query.Submit();

            while (!query.Complete && query.Healthy)
            {
                query.ProcessResults();
                System.Threading.Thread.Sleep(5000);
            }
            
            if (!query.ProcessResults())
            {
                Console.WriteLine("Query failed " + query);
            }

            foreach (XUID x in query.ResultList)
                Console.WriteLine(x);
            
            Console.WriteLine("Found " + query.ResultCount + " xsets that match the query.\n");
        }

        static void RetentionTest()
        {
            xset = xsys.OpenXSet(xuid, Constants.XSET_MODE_MODIFY);
            
            
            Console.Write("Enter the number of days for the Base retention: ");
            string days = Console.ReadLine();

            xset.BaseRetention = Int64.Parse(days) * Constants.RETENTION_DAYS;
            Console.WriteLine(xset.Commit());
            
            XSet.RetentionPolicy actualRetention = xset.GetRetention("base");
            Console.WriteLine(actualRetention);
            

            Console.WriteLine("\nThe following retention Duration schemes are set on the XSystem\n");
            xsys.GetPolicies();
            
            List<XPolicy>.Enumerator iter = xsys.PolicyEnumerator;

            while (iter.MoveNext())
            {
                if (iter.Current.Type == XPolicyType.RetentionDuration)
                    Console.WriteLine(iter.Current.Name);
            }

            Console.WriteLine("\nThe following retention schemes are set on the XSet\n");
            ViewFields(xset, Constants.XSET_RETENTION_LIST);

            Console.Write("\nEnter the name of the Base Retention Duration policy to be applied: ");
            string name = Console.ReadLine();

            if (name != "")
            {
                xset.ApplyPolicy(XPolicyType.BaseRetention, name);
                Console.WriteLine(xset.Commit());

                XSet.RetentionPolicy ri = xset.GetRetention("base");

                Console.WriteLine("\n" + ri);

                actualRetention = xset.GetRetention("base");
                Console.WriteLine("\n Actual duration " + actualRetention.Duration);
            }

            // Event Based Retention
            Console.Write("\nEnter the name of the Event Retention Duration policy to be applied: ");
            name = Console.ReadLine();

            if (name != "")
            {
                XSet.RetentionPolicy xsr = xset.CreateRetention("event");

                // We have to set the enabled flag before the duration??
                xsr.Enabled = true; // Equivalent to xsr.SetEnable(false, true);

                /***** We don't have any RetentionEnabled Policies on the cluster or we could have set it like this ****
                
                Console.WriteLine("\nThe following retention Enabled schemes are set on the XSystem\n");
                iter = xsys.PolicyEnumerator;

                while (iter.MoveNext())
                {
                    if (iter.Current.Type == XPolicyType.RetentionEnabled)
                        Console.WriteLine(iter.Current.Name);
                }
                Console.Write("\nEnter the name of the Enabled policy to be applied: ");
                name = Console.ReadLine();

                xsr.ApplyPolicy(XPolicyType.RetentionEnabled, name;
                
                *****************************************************************************************************/

                xsr.ApplyPolicy(XPolicyType.RetentionDuration, name);
                Console.WriteLine(xset.Commit());

                xsr.Start();
                Console.WriteLine(xset.Commit());

                actualRetention = xset.GetRetention("event");
                Console.WriteLine("\n Actual duration " + actualRetention.Duration);
            }
            

            Console.Write("\nShred " + xset.Shred + " Auto delete " + xset.AutoDelete);
        }

        static void PolicyTest(XAMObject xobj)
        {
            // Display the retention fields already set
            Console.WriteLine("\nThe following policies are available on the object\n");
            xobj.GetPolicies();
            List<XPolicy>.Enumerator xit = xobj.PolicyEnumerator;

            while (xit.MoveNext())
            {
                Console.WriteLine(xit.Current);
            }
        }

        static void ExportTest()
        {
            Console.WriteLine("Exporting xuid (" + xuid + ")");

            using (xset = xsys.OpenXSet(xuid, Constants.XSET_MODE_READ_ONLY))
            {
                using (XStream stream = xset.OpenExportXStream())
                {
                    using (FileStream fs = new FileStream("c:\\exportStream", FileMode.Create))
                    {

                        BinaryReader sr = new BinaryReader(stream, System.Text.Encoding.UTF8);
                        BinaryWriter sw = new BinaryWriter(fs, System.Text.Encoding.UTF8);

                        byte[] buffer = new byte[100 * 1024];
                        int read = 0;

                        while ((read = sr.Read(buffer, 0, 100 * 1024)) > 0)
                        {
                            sw.Write(buffer, 0, read);
                            Console.WriteLine("Read " + read + " bytes for exporting");
                        }

                        Console.WriteLine("\nWrote " + fs.Position + " bytes to the ExportXStream from the XSet");

                        sr.Close();
                        sw.Close();
                    }
                }
            }
        }

        static void ImportTest()
        {
            using (xset = xsys.CreateXSet())
            {
                Console.Write("Full path of export file [c:\\exportStream] : ");
                string fileName = Console.ReadLine();

                if (fileName == "")
                    fileName = "c:\\exportStream";

                try
                {
                    using (FileStream fs = new FileStream(fileName, FileMode.Open))
                    {
                        using (XStream stream = xset.OpenImportXStream())
                        {
                            BinaryReader sr = new BinaryReader(fs, System.Text.Encoding.UTF8);
                            BinaryWriter sw = new BinaryWriter(stream, System.Text.Encoding.UTF8);
                            byte[] buffer = new byte[100 * 1024];
                            int read = 0;

                            while ((read = sr.Read(buffer, 0, 100 * 1024)) > 0)
                            {
                                sw.Write(buffer, 0, read);
                                Console.WriteLine("Read " + fs.Position + " bytes from the ImportXStream and wrote to the XSet ");
                            }

                            sr.Close();
                            sw.Close();
                        }
                    }

                    xuid = xset.Commit();
                    Console.WriteLine(xuid);

                }
                catch (System.IO.FileNotFoundException e)
                {
                    Console.WriteLine("Export stream file " + fileName + " does not exist");
                }
            }
        }

        static void DeleteXSetTest()
        {
            try
            {
                xsys.DeleteXSet(xuid);
            }
            catch(XAMException e)
            {
                Console.WriteLine("Failed to delete xuid [" + e.Status + "] " + e.Message);
            }
        }

        static void MultiPartWriteTest()
        {
            using (xset = xsys.CreateXSet())
            {
                using (Helpers.MultipartXStream xstream = xset.CreateMultipartXStream("graham.test.multipart.stream", "application/binary", 5, 64 * 1024))
                {
                    byte[] myBytes = new byte[64 * 1024];
                    for (int i = 0; i < 64 * 1024; i++)
                    {
                        int j;
                        Math.DivRem(i, 64, out j);

                        myBytes[i] = (byte)('A' + j + (i / 1024));
                    }
                    xstream.Write(myBytes, 0, 64 * 1024);
                }
                xuid = xset.Commit();
            }
        }

        static void MultiPartReadTest()
        {
            using (xset = xsys.OpenXSet(xuid, Constants.XSET_MODE_READ_ONLY))
            {
                using (Helpers.MultipartXStream xstr = xset.OpenMultipartXStream("graham.test.multipart.stream"))
                {
                    byte[] myBytes = new byte[64 * 1024];
                    xstr.Read(myBytes, 0, 64 * 1024);
                }
            }
        }

        static void MultiPartWriteTest2()
        {
            using (xset = xsys.CreateXSet())
            {
                using (FileStream s = new FileStream("c:\\mpwriteTest.in", FileMode.Open))
                {
                    xset.MultipartXStreamWrite(s, "graham.test.multipart.stream", "application/binary", 15, true);
                }
                xuid = xset.Commit();
            }
        }

        static void MultiPartReadTest2()
        {
            using (xset = xsys.OpenXSet(xuid, Constants.XSET_MODE_READ_ONLY))
            {
                using (FileStream s = new FileStream("c:\\mpwriteTest.out", FileMode.Create))
                {
                    s.Position = 0;

                    xset.MultipartXStreamRead(s, "graham.test.multipart.stream");
                }
            }
        }

        static void GetXUID()
        {
            Console.Write("Enter base64 encoded xuid or Centera ClipID [" + xuid + "]: ");
            string answer = Console.ReadLine();

            if (answer != "")
            {
                xuid = new XUID(answer);
            }
        }

        static void ReleaseHold()
        {
            Console.Write("Enter xuid [" + xuid + "]: ");
            String newXuid = Console.ReadLine();

            if (newXuid != "")
                xuid = new XUID(newXuid);

            Console.Write("Enter hold ID: ");
            String holdID = Console.ReadLine();

            xsys.ReleaseXSet(xuid, holdID);
        }

        static void SetHold()
        {
            Console.Write("Enter xuid [" + xuid + "]: ");
            String newXuid = Console.ReadLine();

            if (newXuid != "")
                xuid = new XUID(newXuid);

            Console.Write("Enter hold ID: ");
            String holdID = Console.ReadLine();

            xsys.HoldXSet(xuid, holdID);
        }

        static void SimpleXAMTest()
        {
            try
            {
                string connectString =
                    //"snia-xam://centera_vim!emea1?c:\\ixos";  // EMEA1
                    //"snia-xam://centera_vim!us2cas1.centera.org?c:\\peas\\us2.pea";  // US2
                    "snia-xam://centera_vim!128.221.200.60?c:\\peas\\xamconnect.pea";
                    //"snia-xam://centera_vim!cso";
                XAMLibrary xlib = XAMLibrary.Instance;

                xlib.LogLevel = Constants.LOG_ALL;
                xlib.LogPath = "sdk.log";

                xlib.CreateField(Constants.CENTERA_APP_NAME, "XAM.NET Test Harness", false);
                xlib.CreateField(Constants.CENTERA_APP_VERSION, "3.5", false);

                using (xsys = xlib.CreateXSystem(connectString))
                {
                    if (!xsys.Authenticated)
                        // Authenticate - can provide an authentication string or use default of ANONYMOUS
                        xsys.Authenticate();

                    int choice = 0;

                    do
                    {
                        Console.WriteLine("\n\n\nTests available to perform");
                        Console.WriteLine("==========================\n");
                        Console.WriteLine("1. View fields on the XAM Library Handle");
                        Console.WriteLine("2. View fields on the XSystem connection");
                        Console.WriteLine("3. Create an XSet");
                        Console.WriteLine("4. Open an existing XSet");
                        Console.WriteLine("5. All Asynchronous methods");
                        Console.WriteLine("6. Query");
                        Console.WriteLine("7. Retention");
                        Console.WriteLine("8. Policies");
                        Console.WriteLine("9. Export");
                        Console.WriteLine("10 Import");
                        Console.WriteLine("11 DeleteXSet");
                        Console.WriteLine("12 MultiPartWrite (Serial)");
                        Console.WriteLine("13 MultiPartRead (Serial)");
                        Console.WriteLine("14 MultiPartWrite (Multi threaded)");
                        Console.WriteLine("15 MultiPartRead (Multi threaded)");
                        Console.WriteLine("16 Set Hold");
                        Console.WriteLine("17 Release Hold");
                        Console.WriteLine("18 Show Content Address of XUID");

                        Console.Write("Choice: ");
                        
                        string s = Console.ReadLine();
                        Console.WriteLine();
                        if (s == "")
                            break;

                        choice = Int16.Parse(s);

                        switch (choice)
                        {
                            case 1: ViewFields(xlib, "");
                                break;

                            case 2: ViewFields(xsys, "");
                                break;

                            case 3: XSetCreateTest();
                                Console.WriteLine(xuid);
                                break;

                            case 4: 
                                GetXUID();
                                XSetOpenTest();
                                break;

                            case 5: xuid = AsyncCreateTest();
                                Console.WriteLine(xuid);
                                xset = AsyncOpenTest();
                                ViewFields(xset, ".vnd");
                                xset.Dispose();
                                break;

                            case 6: QueryTest();
                                break;

                            case 7:
                                GetXUID();
                                RetentionTest();
                                break;

                            case 8: PolicyTest(xsys);
                                break;

                            case 9:
                                GetXUID();
                                ExportTest();
                                break;

                            case 10: ImportTest();
                                break;

                            case 11: GetXUID();
                                DeleteXSetTest();
                                break;

                            case 12: MultiPartWriteTest();
                                break;

                            case 13: GetXUID();
                                MultiPartReadTest();
                                break;

                            case 14: MultiPartWriteTest2();
                                break;

                            case 15:
                                GetXUID();
                                MultiPartReadTest2();
                                break;

                            case 16:
                                SetHold();
                                break;

                            case 17:
                                ReleaseHold();
                                break;

                            case 18:
                                GetXUID();
                                Console.WriteLine(xuid.ContentAddress);
                                break;
                        }
                    } while (choice > 0 && choice < 19);
                }
            }
            catch (Exception e)
            {
                Console.Error.WriteLine(e);
            }
            finally
            {
            }
        }

        static void Main(string[] args)
        {
            try
            {
                SimpleXAMTest();
            }
            catch (Exception e)
            {
                Console.WriteLine(e);
            }
        }
    }
}
